#pragma once
#include <iostream>
#include <vector>
#include <string>
#include <queue>
#include "Thread.hpp"
#include "Mutex.hpp"
#include "Cond.hpp"
#include "MyLog.hpp"

namespace MyThreadPool
{
    using namespace MyThread;
    using namespace MyMutex;
    using namespace MyCond;
    using namespace MyLog;

    static const int defaultnum = 5;

    template <typename T>
    class ThreadPool
    {
    private:
        void WakeUpAll()
        {
            LockGuard lg(&_mutex);
            if (_sleep_num > 0) // 有线程在休眠
            {
                LOG(LogLevel::DEBUG) << "唤醒所有线程";
                _cond.Broadcast(); // 唤醒所有线程
            }
        }

        void WakeUpOne()
        {
            LOG(LogLevel::DEBUG) << "唤醒一个线程";
            _cond.Signal();
        }

        void Start() // Start函数最好也设为私有
        {
            if (_isrunning)
                return; // 线程已经启动就不要重复启动
            _isrunning = true;
            for (auto &thread : _threads)
            {
                thread.Start();
            }
        }

        ThreadPool(int num = defaultnum) // 构造函数私有
            : _num(num),
              _isrunning(false),
              _sleep_num(0)
        {
            for (int i = 0; i < _num; i++)
            {
                _threads.emplace_back([this]()
                                      { Handler(); }); // Lambda表达式
            }
        }

        // 禁用赋值和拷贝
        ThreadPool(const ThreadPool<T> &) = delete;
        ThreadPool<T> &operator=(const ThreadPool<T> &) = delete;

    public:
        // static的类不能访问类成员变量和函数，但是可以访问被static修饰的
        static ThreadPool<T> *GetInstance()
        {
            if (_inc == nullptr)
            {
                LockGuard lg(&_lock); // 保护单例
                if (_inc == nullptr)
                {
                    LOG(LogLevel::DEBUG) << "首次使用, 创建对象";
                    _inc = new ThreadPool<T>; // 创建对象
                    _inc->Start();            // 创建之后就启动
                }
                LOG(LogLevel::DEBUG) << "获取线程池单例";
            }
            return _inc;
        }

        void Handler() // 返回值为void，参数为空
        {
            char name[128];
            pthread_getname_np(pthread_self(), name, sizeof(name)); // 获得线程名
            while (true)
            {
                T t;
                {
                    LockGuard lg(&_mutex);              // 加锁
                    while (_task.empty() && _isrunning) // 队列不为空并且没被退出
                    {
                        _sleep_num++;
                        _cond.Wait(_mutex); // 在条件变量下等
                        _sleep_num--;
                    }

                    if (!_isrunning && _task.empty())
                    {
                        LOG(LogLevel::INFO) << name << "已退出, 任务队列无数据";
                        break;
                    }
                    t = _task.front(); // 获取任务
                    _task.pop();
                }
                t(); // 处理任务时不用在临界区内部
            }
        }

        void Join()
        {
            if (_isrunning == true)
                return;
            for (auto &thread : _threads)
            {
                thread.Join();
            }
        }

        void Stop()
        {
            _isrunning = false;
            WakeUpAll();
        }

        bool Equeue(const T &task)
        {
            if (_isrunning)
            {
                LockGuard lg(&_mutex);
                _task.push(task);                  // 往队列里入任务
                if (_threads.size() == _sleep_num) // 线程全在休眠就叫醒一个
                    WakeUpOne();
                return true;
            }
            return false;
        }

        ~ThreadPool() {}

    private:
        std::vector<Thread> _threads; // 管理线程
        int _num;                     // 线程数量
        std::queue<T> _task;          // 任务队列
        Mutex _mutex;                 // 锁
        Cond _cond;                   // 条件变量
        bool _isrunning;              // 是否在运行
        int _sleep_num;               // 在休眠的线程的数量

        static ThreadPool<T> *_inc; // 单例指针
        static Mutex _lock;         // 保护单例的锁
    };

    // static成员在类外初始化
    template <typename T>
    ThreadPool<T> *ThreadPool<T>::_inc = nullptr;

    template <typename T>
    Mutex ThreadPool<T>::_lock;
}